package com.spring.util;

import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.*;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;

@Slf4j
public class ClassUtils {
	/**
	 * 拿到类加载器
	 */
	public static ClassLoader getClassLoader() {
		return ClassUtils.class.getClassLoader();
	}
	
	/**
	 * 加载某个类
	 */
	public static Class<?> loadClass(String className) {
		try {
			return getClassLoader().loadClass(className);
		} catch (ClassNotFoundException e) {
			throw new RuntimeException(className + " 类找不到,无法加载");
		}
	}
	
	/**
	 * 用ClassLoader 加载资源
	 *
	 * @param filepath 文件路径
	 * @return 返回输入流
	 */
	public static InputStream getResource(String filepath) {
		InputStream resourceAsStream = getClassLoader().getResourceAsStream(filepath);
		if (resourceAsStream == null) {
			log.error("{} 文件没有找到", filepath);
		}
		return resourceAsStream;
	}
	
	
	/**
	 * 扫描返回类class 集合
	 */
	public static List<Class<?>> scan(String scanPath) {
		List<Class<?>> bds = new ArrayList<>(16);
		try {
			//  com.spring.io 这种类路径替换为实际的文件路径，这里不考虑 jar 包内部的class文件
			Enumeration<URL> resources = getClassLoader().getResources(scanPath.replace(".", "/"));
			while (resources.hasMoreElements()) {
				URL url = resources.nextElement();
				
				// URL路径如果含有中文 ，需要解码才能使用
				String decodePath = URLDecoder.decode(url.getPath(), "utf-8");
				File rootFile = new File(decodePath);
				
				File[] files = rootFile.listFiles();
				if (files == null) {
					continue;
				}
				for (File file : files) {
					// 文件夹则递归调用下一层级扫描
					if (file.isDirectory()) {
						bds.addAll(scan(scanPath + "." + file.getName()));
					} else if (file.isFile() && file.getName().endsWith(".class")) {
						String fullClassName = scanPath + "." + file.getName().substring(0, file.getName().length() - 6);
						Class<?> clazz = loadClass(fullClassName);
						bds.add(clazz);
					}
				}
			}
		} catch (IOException e) {
			log.error("扫描包时出现错误", e);
		}
		return bds;
	}
	
	
	public static List<Field> findField(Class<?> clazz) {
		List<Field> allFields = new ArrayList<>(16);
		
		// 获取自身的所有字段，不含继承的，继承字段需要上父类中获取
		Field[] privateFields = clazz.getDeclaredFields();
		allFields.addAll(Arrays.asList(privateFields));
		
		Class<?> parent = clazz.getSuperclass();
		while (parent != Object.class) {
			allFields.addAll(Arrays.asList(parent.getDeclaredFields()));
			
			// 继续向上查找
			parent = parent.getSuperclass();
		}
		return allFields;
	}
	
	/**
	 * 通用字段赋值
	 *
	 * @param targetObj 目标对象
	 * @param field     要赋值的目标成员变量
	 * @param val       要赋的值
	 */
	public static void assignValueToField(Object targetObj, Field field, Object val) {
		String setterMethodName = StringUtils.getSetterMethodName(field.getName());
		Class<?> clazz = targetObj.getClass();
		try {
			Method method = clazz.getMethod(setterMethodName, val.getClass());
			method.invoke(targetObj, val);
		} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
			try {
				field.setAccessible(true);
				field.set(targetObj, val);
			} catch (IllegalAccessException ex) {
				log.error("反射赋值{}的成员变量{}时失败,setter方法和直接反射均失败", clazz.getName(), field.getName());
			}
		}
	}
	
}

